package com.newly.center.sys.service.authEntity.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.newly.api.business.feign.CompanyClient;
import com.newly.api.business.feign.MQTTDeviceClient;
import com.newly.api.business.feign.SiteClient;
import com.newly.center.sys.service.auth.api.IAuthService;
import com.newly.center.sys.service.authEntity.api.IAuthEntityService;
import com.newly.center.sys.service.domain.api.IDomainService;
import com.newly.center.sys.service.menu.api.IMenuService;
import com.newly.center.sys.service.tenant.api.ITenantService;
import com.newly.common.base.constants.AuthConstant;
import com.newly.common.base.entity.R;
import com.newly.common.base.entity.auth.ICurrentTenant;
import com.newly.common.base.entity.business.Po.Company;
import com.newly.common.base.entity.business.Po.Site;
import com.newly.common.base.entity.device.po.DevicePo;
import com.newly.common.base.entity.sys.auth.entity.bo.AuthEntityBo;
import com.newly.common.base.entity.sys.auth.entity.bo.AuthEntityTypeBo;
import com.newly.common.base.entity.sys.auth.entity.po.AuthEntityPo;
import com.newly.common.base.entity.sys.domain.po.DomainPo;
import com.newly.common.base.entity.sys.menu.po.MenuPo;
import com.newly.common.base.entity.sys.tenant.po.TenantPo;
import com.newly.common.core.utils.ConverseUtils;
import com.newly.common.core.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: newly-parent
 * @remark: 权限实体业务实现
 * @author: guan
 * @create: 2022-01-13 11:46
 */
@Service
@Slf4j
public class AuthEntityService extends AbstractAuthEntityService implements IAuthEntityService {

    @Autowired
    private IAuthService authService;

    @Autowired
    private ITenantService tenantService;

    @Autowired
    private IDomainService domainService;

    @Autowired
    private IMenuService menuService;

    @Autowired
    private CompanyClient companyClient;

    @Autowired
    private SiteClient siteClient;

    @Autowired
    private MQTTDeviceClient mqttDeviceClient;

    @Autowired
    private RequestUtils requestUtils;

    @Override
    public List<AuthEntityPo> getByRoleIds(List<Integer> roleIds) {
        if (ObjectUtil.isEmpty(roleIds)) return null;
        LambdaQueryWrapper<AuthEntityPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(AuthEntityPo::getRoleId, roleIds);
        return authEntityMapper.selectList(wrapper);
    }

    @Override
    public List<AuthEntityPo> getByRoleId(Integer roleId) {
        return getByRoleIds(Arrays.asList(roleId));
    }

    @Override
    public boolean bind(AuthConstant.AuthEntityType entityType, Integer roleId, String entityId) {
        if (ObjectUtil.isEmpty(roleId)) {
            ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
            roleId = currentTenant.getSelfRoleId();
        }
        int result = this.insert(new AuthEntityPo(entityType, roleId, entityId));
        if (result > 0) return authService.pushAuthPayloads(entityType, entityId);
        return false;
    }

    @Override
    public boolean bind(AuthConstant.AuthEntityType entityType, Integer roleId,
                        List<String> entityIds) {
        if (ObjectUtil.isEmpty(entityIds)) return true;
        for (String entityId : entityIds) {
            bind(entityType, roleId, entityId);
        }
        return true;
    }

    @Override
    public boolean bind(AuthConstant.AuthEntityType entityType, String entityId) {
        return bind(entityType, Arrays.asList(entityId));
    }

    @Override
    public boolean bind(AuthConstant.AuthEntityType entityType, List<String> entityIds) {
        return bind(entityType, null, entityIds);
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public boolean unBind(AuthConstant.AuthEntityType entityType, Integer roleId,
                          List<String> entityIds) {
        if (ObjectUtil.isEmpty(entityIds)) return false;
        LambdaQueryWrapper<AuthEntityPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AuthEntityPo::getType, entityType.getCode());
        if (!ObjectUtil.isEmpty(roleId)) {
            wrapper.eq(AuthEntityPo::getRoleId, roleId);
        }
        wrapper.in(AuthEntityPo::getEntityId, entityIds);
        int result = authEntityMapper.delete(wrapper);
        if (result > 0) return authService.popAuthPayloads(entityType, entityIds);
        return false;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public boolean unBind(AuthConstant.AuthEntityType entityType, List<String> entityIds) {
        return unBind(entityType, null, entityIds);
    }

    @Override
    public boolean unBind(Integer roleId, AuthConstant.AuthEntityType entityType) {
        LambdaQueryWrapper<AuthEntityPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AuthEntityPo::getType, entityType.getCode());
        wrapper.eq(AuthEntityPo::getRoleId, roleId);
        int result = authEntityMapper.delete(wrapper);
        if (result > 0) {
            return authService.flushAuthPayloads();
        }
        return true;
    }

    @Override
    public boolean removeByRoleIds(List<Integer> roleIds) {
        if (ObjectUtil.isEmpty(roleIds)) return false;
        LambdaQueryWrapper<AuthEntityPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(AuthEntityPo::getRoleId, roleIds);
        int delete = authEntityMapper.delete(wrapper);
        return delete > 0;
    }

    @Override
    public boolean removeByRoleId(Integer roleId) {
        return removeByRoleIds(Arrays.asList(roleId));
    }

    @Override
    public List<AuthEntityTypeBo> getTypes() {
        AuthConstant.AuthEntityType[] values = AuthConstant.AuthEntityType.values();
        List<AuthEntityTypeBo> authEntityTypeBos = new ArrayList<>(values.length);
        for (AuthConstant.AuthEntityType value : values) {
            if (value.getCode() == AuthConstant.AuthEntityType.MENU.getCode()) {
                continue;
            }
            authEntityTypeBos.add(new AuthEntityTypeBo(value));
        }
        return authEntityTypeBos;
    }

    @Override
    public List<AuthEntityBo> getByTypeCode(Integer typeCode) {
        if (ObjectUtil.isEmpty(typeCode)) return null;

        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        List<AuthEntityBo> authEntityBos = new ArrayList<>();
        if (typeCode.equals(AuthConstant.AuthEntityType.TENANT.getCode())) {
            // 租户
            List<String> entityIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.TENANT);
            List<TenantPo> tenantPos = tenantService.getByIds(ConverseUtils.toLong(entityIds));
            List<AuthEntityBo.EntityData> entityDatum = new ArrayList<>();
            for (TenantPo tenantPo : tenantPos) {
                entityDatum.add(new AuthEntityBo.EntityData(tenantPo));
            }
            authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.TENANT, entityDatum));
        } else if (typeCode.equals(AuthConstant.AuthEntityType.DOMAIN.getCode())) {
            // 域
            List<String> entityIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.DOMAIN);
            List<DomainPo> domainPos = domainService.getByIds(entityIds);
            List<AuthEntityBo.EntityData> entityDatum = new ArrayList<>();
            for (DomainPo domainPo : domainPos) {
                entityDatum.add(new AuthEntityBo.EntityData(domainPo));
            }
            authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.DOMAIN, entityDatum));
        }

        return authEntityBos;
    }

    @Override
    public List<AuthEntityBo> listAuthEntity() {
        List<AuthEntityBo> authEntityBos = new ArrayList<>();

        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        // 获取菜单
        List<String> menuIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.MENU);
        if (!ObjectUtil.isEmpty(menuIds)) {
            List<MenuPo> menuPos = menuService.getPermListInAuth();
            if (!ObjectUtil.isEmpty(menuPos)) {
                List<AuthEntityBo.EntityData> entityDatum = new ArrayList<>();
                for (MenuPo menuPo : menuPos) {
                    entityDatum.add(new AuthEntityBo.EntityData(menuPo));
                }
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.MENU, entityDatum));
            }
        } else {
            authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.MENU, new ArrayList<>()));
        }

        // 获取企业
        List<String> companyIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.COMPANY);
        if (!ObjectUtil.isEmpty(companyIds)) {
            R r = companyClient.queryByIdsInAuth(companyIds);
            if (!ObjectUtil.isEmpty(r) && !ObjectUtil.isEmpty(r.getData())) {
                List<Company> companies = BeanUtil.copyToList((List<Company>) r.getData(), Company.class);
                List<AuthEntityBo.EntityData> entityDatum = new ArrayList<>();
                for (Company company : companies) {
                    entityDatum.add(new AuthEntityBo.EntityData(company));
                }
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.COMPANY, entityDatum));
            } else {
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.COMPANY, new ArrayList<>()));
            }
        } else {
            authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.COMPANY, new ArrayList<>()));
        }

        // 获取站点
        List<String> siteIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.SITE);
        if (!ObjectUtil.isEmpty(siteIds)) {
            R r = siteClient.queryByIdsInAuth(siteIds);
            if (!ObjectUtil.isEmpty(r) && !ObjectUtil.isEmpty(r.getData())) {
                List<Site> sites = BeanUtil.copyToList((List<Site>) r.getData(), Site.class);
                List<AuthEntityBo.EntityData> entityDatum = new ArrayList<>();
                for (Site site : sites) {
                    entityDatum.add(new AuthEntityBo.EntityData(site));
                }
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.SITE, entityDatum));
            } else {
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.SITE, new ArrayList<>()));
            }
        } else {
            authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.SITE, new ArrayList<>()));
        }

        // 获取设备
        List<String> deviceIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.DEVICE);
        if (!ObjectUtil.isEmpty(deviceIds)) {
            R r = mqttDeviceClient.queryByIdsInAuth(deviceIds);
            if (!ObjectUtil.isEmpty(r) && !ObjectUtil.isEmpty(r.getData())) {
                List<DevicePo> devicePos = BeanUtil.copyToList((List<DevicePo>) r.getData(), DevicePo.class);
                List<AuthEntityBo.EntityData> entityDatum = new ArrayList<>();
                for (DevicePo devicePo : devicePos) {
                    entityDatum.add(new AuthEntityBo.EntityData(devicePo));
                }
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.DEVICE, entityDatum));
            } else {
                authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.DEVICE, new ArrayList<>()));
            }
        } else {
            authEntityBos.add(new AuthEntityBo(AuthConstant.AuthEntityType.DEVICE, new ArrayList<>()));
        }
        return authEntityBos;
    }

    @Override
    public List<String> getIdsByRoleIdAndTypeCode(Integer roleId, Integer typeCode) {
        AuthConstant.AuthEntityType entityType = AuthConstant.AuthEntityType.getInstance(typeCode);
        if (ObjectUtil.isEmpty(entityType)) return null;

        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        List<String> entityIds = currentTenant.getEntityIds(roleId, entityType);
        if (!ObjectUtil.isEmpty(entityIds)) {
            entityIds = entityIds.stream().distinct().collect(Collectors.toList());
        }
        return entityIds;
    }

    @Override
    public Map<Integer, List<String>> getAllIdsByRoleId(Integer roleId) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();

        Map<Integer, List<String>> entityIdMap = new HashMap<>();
        // 获取菜单
        List<String> menuIds = currentTenant.getEntityIds(roleId,AuthConstant.AuthEntityType.MENU);
        if (!ObjectUtil.isEmpty(menuIds)) {
            entityIdMap.put(AuthConstant.AuthEntityType.MENU.getCode(),
                    menuIds.stream().distinct().collect(Collectors.toList()));
        } else {
            entityIdMap.put(AuthConstant.AuthEntityType.MENU.getCode(), new ArrayList<>());
        }

        // 获取企业
        List<String> companyIds = currentTenant.getEntityIds(roleId,AuthConstant.AuthEntityType.COMPANY);
        if (!ObjectUtil.isEmpty(companyIds)) {
            entityIdMap.put(AuthConstant.AuthEntityType.COMPANY.getCode(),
                    companyIds.stream().distinct().collect(Collectors.toList()));
        } else {
            entityIdMap.put(AuthConstant.AuthEntityType.COMPANY.getCode(), new ArrayList<>());
        }

        // 获取站点
        List<String> siteIds = currentTenant.getEntityIds(roleId,AuthConstant.AuthEntityType.SITE);
        if (!ObjectUtil.isEmpty(siteIds)) {
            entityIdMap.put(AuthConstant.AuthEntityType.SITE.getCode(),
                    siteIds.stream().distinct().collect(Collectors.toList()));
        } else {
            entityIdMap.put(AuthConstant.AuthEntityType.SITE.getCode(), new ArrayList<>());
        }

        // 获取设备
        List<String> deviceIds = currentTenant.getEntityIds(roleId,AuthConstant.AuthEntityType.DEVICE);
        if (!ObjectUtil.isEmpty(deviceIds)) {
            entityIdMap.put(AuthConstant.AuthEntityType.DEVICE.getCode(),
                    deviceIds.stream().distinct().collect(Collectors.toList()));
        } else {
            entityIdMap.put(AuthConstant.AuthEntityType.DEVICE.getCode(), new ArrayList<>());
        }

        return entityIdMap;
    }
}
